

#include "Database.h"


Database::Database(){
	timer = new Timer();
	sqlite3_open(":memory:", &memorydb);
}

Database::~Database(){

	std::string query;
	char* err = NULL;
	for (std::set<int>::iterator it = typeSet.begin(); it != typeSet.end(); it++) {
		std::string dbTableName;
		dbTableName = util::intToString(*it);
		string query = "DROP TABLE ";
		query += "table" + dbTableName;
		sqlite3_exec(memorydb, query.c_str(), NULL, NULL, &err);
	}
	sqlite3_close(memorydb);

	for(boost::unordered_map<key_t, PubPkt*>::iterator it = keyToPktMap.begin(); it != keyToPktMap.end(); it++){
		delete it->second;
	}
}


bool Database::insert(PubPkt *pkt, key_t k){
	timer->start();

	char* err = NULL;
	char* errCreate = NULL;
	std::string query;
	std::string queryCreate;

	std::set<int>::iterator it = typeSet.find(pkt->getEventType());
	if (it != typeSet.end()){
		cout << "Memory: Database: start query creation..." << endl;
		std::string dbTableName = util::intToString(*it);
		std::string query = "INSERT INTO ";
		query += "table" + dbTableName;
		query += " VALUES(";
		query += util::intToString(k);
		query += ", ";
		for (int i=0; i<pkt->getAttributesNum(); i++){
			Attribute attribute = pkt->getAttribute(i);
			if (attribute.type == INT) {
				query += util::intToString(attribute.intVal);
				query += ", ";
			}
			if (attribute.type == FLOAT) {
				query += util::floatToString(attribute.floatVal);
				query += ", "; }
			if (attribute.type == BOOL) {
				query += util::boolToString(attribute.boolVal);
				query += ", "; }
			if (attribute.type == STRING) {
				query += "'";
				query += attribute.stringVal;
				query += "'";
				query += ", ";
			}
		}
		query += util::longToString(pkt->getTimeStamp().getTimeVal()) + ")";
	//	cout << query << endl;
		cout << "Memory: Database: query created." << endl;
		sqlite3_exec(memorydb, query.c_str(), NULL, NULL, &err);
		cout << "Memory: Database: query executed." << endl;
	} else {
		cout << "Memory: Database: start table creation..." << endl;
		cout << "Memory: Database: start query creation..." << endl;
		typeSet.insert(pkt->getEventType());
		std::string dbTableName = util::intToString(pkt->getEventType());
		queryCreate = "CREATE TABLE ";
		queryCreate += "table" + dbTableName;
		queryCreate += "(key INT, "; // PRIMARY KEY
		query = "INSERT INTO ";
		query += "table" + dbTableName;
		query += " VALUES(";
		query += util::intToString(k);
		query += ", ";
		for (int i=0; i<pkt->getAttributesNum(); i++){
			Attribute attribute = pkt->getAttribute(i);
			if (attribute.type == INT) {
				query += util::intToString(attribute.intVal) + ", ";
				queryCreate += attribute.name;
				queryCreate += " INT, ";
			}
			if (attribute.type == FLOAT) {
				query += util::floatToString(attribute.floatVal);
				query += ", ";
				queryCreate += attribute.name;
				queryCreate += " FLOAT, ";
			}
			if (attribute.type == BOOL){
				query += util::boolToString(attribute.boolVal);
				query += ", ";
				queryCreate += attribute.name;
				queryCreate += " BOOLEAN, ";
			}
			if (attribute.type == STRING){
				query += "'";
				query += attribute.stringVal;
				query += "'";
				query += ", ";
				queryCreate += attribute.name;
				queryCreate += " VARCHAR(";
				queryCreate += util::intToString(STRING_VAL_LEN);
				queryCreate += "), ";
			}
		}
		query += util::longToString(pkt->getTimeStamp().getTimeVal()) + ")";
		queryCreate += "timestamp UNSIGNED BIG INT)";
	//	cout << queryCreate << endl;
	//	cout << query << endl;
		cout << "Memory: Database: query created." << endl;
		sqlite3_exec(memorydb, queryCreate.c_str(), NULL, NULL, &errCreate);
		cout << "Memory: Database: table created." << endl;
		sqlite3_exec(memorydb, query.c_str(), NULL, NULL, &err);
		cout << "Memory: Database: query executed." << endl;
	}
	keyToPktMap.insert(make_pair(k, pkt));
	keyTypeMatchingMap.insert(make_pair(k, pkt->getEventType()));
	if (err != NULL || errCreate != NULL) {
		cout << "Memory: Database: insertion failed: " << err << endl;
		return false;
	}

	timer->stop();
	cout << "Memory: Database: insertion terminated in " << timer->getElapsedTimeInMilliSec() << " ms." << endl;
	return true;
}


bool Database::remove(std::pair<timestamp_t, key_t> k){
	timer->start();

	char* err = NULL;
	std::string query;

	boost::unordered_map<key_t, int>::iterator it = keyTypeMatchingMap.find(k.second);
	if (it != keyTypeMatchingMap.end()){
		cout << "Memory: Database: start query creation..." << endl;
		std::string dbTableName = util::intToString(it->second);
		query = "DELETE FROM ";
		query += "table" + dbTableName;
		query += " WHERE key=";
		query += util::intToString(k.second);
		//cout << query << endl;
		cout << "Memory: Database: query created." << endl;
		sqlite3_exec(memorydb, query.c_str(), NULL, NULL, &err);
		cout << "Memory: Database: query executed." << endl;
		if (err != NULL){
			cout << "Memory: Database: remotion failed: " << err << endl;
			return false;
		}
		keyTypeMatchingMap.erase(it);
		delete keyToPktMap.find(k.second)->second;
		keyToPktMap.erase(k.second);
		timer->stop();
		cout << "Memory: Database: remotion terminated in " << timer->getElapsedTimeInMilliSec() << " ms." << endl;
		return true;
	}
	cout << "Memory: Database: remotion failed: message not found." << endl;
	return false;
}


/*std::set<PubPkt*>*/void Database::extract(int type, Constraint* constraint, int numConstraint, timestamp_t entryTimestamp, float window, OccurrenceType occurrence, std::set<PubPkt*> &resultSet){

	timer->start();

	char* err = NULL;
	std::string query;
	std::string antequery;
	std::string middlequery;
	std::string postquery;

//	std::set<PubPkt*> resultSet;


	ParamType* param = (ParamType*)malloc(sizeof(ParamType));
	param->resultSet = &resultSet;
	param->keyToPktMap = &keyToPktMap;

	if (typeSet.find(type) == typeSet.end()){
		timer->stop();
		cout << "Memory: Database: extraction terminated in " << timer->getElapsedTimeInMilliSec() << " ms." << endl;
//		return resultSet;
		return;
	}

	cout << "Memory: Database: start query creation..." << endl;
	antequery = "SELECT key FROM ";
	middlequery = "table" + util::intToString(type);
	middlequery += " WHERE ";
	for (int i=0; i<numConstraint; i++){
		middlequery += constraint[i].name;
		if (constraint[i].op == EQ) middlequery += "=";
		if (constraint[i].op == LT) middlequery += "<=";
		if (constraint[i].op == GT) middlequery += ">=";
		if (constraint[i].type == INT) {
			middlequery += util::intToString(constraint[i].intVal);
			middlequery += " AND ";
		}
		if (constraint[i].type == FLOAT) {
			middlequery += util::floatToString(constraint[i].floatVal);
			middlequery += " AND ";
		}
		if (constraint[i].type == BOOL) {
			middlequery += util::boolToString(constraint[i].boolVal);
			middlequery += " AND ";
		}
		if (constraint[i].type == STRING) {
			middlequery += "'";
			middlequery += constraint[i].stringVal;
			middlequery += "'";
			middlequery += " AND ";
		}
	}
	postquery = "timestamp<=";
	postquery += util::longToString(entryTimestamp);
	postquery += " AND timestamp>=";
	postquery += util::longToString(entryTimestamp - (int long)window);

	if (occurrence == LAST) {
		query = antequery + middlequery + "timestamp = (SELECT max(timestamp) FROM " + middlequery + postquery + ")";
	} else {
		query = antequery + middlequery + postquery + " ORDER BY timestamp";
	}
//	cout << query << endl;
	cout << "Memory: Database: query created." << endl;
	sqlite3_exec(memorydb, query.c_str(), db::addToResultSet, (void*)param, &err);
	cout << "Memory: Database: query executed." << endl;
	timer->stop();

	cout << "Memory: Database: extraction terminated in " << timer->getElapsedTimeInMilliSec() << " ms." << endl;
//	return resultSet;
}


int db::addToResultSet(void* input, int argc, char **argv, char **column){
	ParamType *param = (ParamType*)input;
	if (argc != 1) return 0;
	boost::unordered_map<key_t, PubPkt*>::iterator it = param->keyToPktMap->find(atoi(argv[0]));
	if (it != param->keyToPktMap->end()){
		param->resultSet->insert(it->second);
	}
//	free (param);
	return 0;
}
